
#include <BPainter>
#include <BKeyEvent>
#include <BMouseEvent>
#include <BStyle>
#include <BFont>

#include <BLongSlider>
#include "member_BLongSlider.h"
#include "member_BStyle.h"

using namespace BWE;

#define member					(*(member_BLongSlider*)_ptr)
#define member_allocate()		_ptr = new member_BLongSlider(this)
#define member_release()		delete (member_BLongSlider*)_ptr

BLongSlider::BLongSlider(BWidget* parent) : BWidget(parent)
{
	member_allocate();
}
BLongSlider::BLongSlider(Orientation orientation, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	setOrientation(orientation);
}
BLongSlider::BLongSlider(BLong minimum, BLong maximum, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	setRange(minimum, maximum);
}
BLongSlider::BLongSlider(BLong minimum, BLong maximum, Orientation orientation, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	setRange(minimum, maximum);
	setOrientation(orientation);
}
BLongSlider::~BLongSlider()
{
	member_release();
}

void BLongSlider::setReadOnly(bool readOnly)
{
	if (member.readOnly != readOnly)
	{
		member.readOnly = readOnly;
		member.hovered = false;
		member.pressed = false;
		this->emit(Signal_ReadOnly, readOnly);
		this->fresh();
	}
}
bool BLongSlider::readOnly() const
{
	return member.readOnly;
}

BReal BLongSlider::ratio() const
{
	BReal size = BReal(member.maximum - member.minimum);
	if (size > REAL_EPSILON)
		return (member.value - member.minimum) / size;
	return 0;
}

void BLongSlider::setOrientation(Orientation orientation)
{
	if (member.orientation != orientation)
	{
		member.orientation = orientation;
		member.dirty = true;
		this->emit(Signal_OrientationChanged, orientation);
		this->fresh();
	}
}
Orientation BLongSlider::orientation() const
{
	return member.orientation;
}

void BLongSlider::setGraph(Graph graph)
{
	if (member.handleGraph != graph)
	{
		member.handleGraph = graph;
		member.dirty = true;
		this->fresh();
	}
}
Graph BLongSlider::graph() const
{
	return member.handleGraph;
}

void BLongSlider::setIconSize(const BSize& iconSize)
{
	if (member.handleSize != iconSize)
	{
		member.handleSize = iconSize;
		member.dirty = true;
		this->fresh();
	}
}
const BSize& BLongSlider::iconSize() const
{
	return member.handleSize;
}

bool BLongSlider::setValue(BLong value)
{
	if (value != bClamp(value, member.minimum, member.maximum))
		return false;
	if (member.value != value)
	{
		member.value = value;
		member.dirty = true;
		this->fresh();
		emit(Signal_ValueChanged, value);
	}
	return true;
}
BLong BLongSlider::value() const
{
	return member.value;
}

bool BLongSlider::setRange(const BRangel& range)
{
	return setRange(range.min(), range.max());
}
bool BLongSlider::setRange(BLong minimum, BLong maximum)
{
	if (minimum > maximum)
		return false;
	if (member.minimum != minimum || member.maximum != maximum)
	{
		if (member.value < minimum)
			member.value = minimum;
		if (member.value > maximum)
			member.value = maximum;
		member.minimum = minimum;
		member.maximum = maximum;
		member.dirty = true;
		this->fresh();
		BRangel range(minimum, maximum);
		emit(Signal_RangeChanged, range);
	}
	return true;
}
BRangel BLongSlider::range() const
{
	return BRangel(member.minimum, member.maximum);
}

bool BLongSlider::setMinimum(BLong minimum)
{
	return setRange(minimum, member.maximum);
}
BLong BLongSlider::minimum() const
{
	return member.minimum;
}

bool BLongSlider::setMaximum(BLong maximum)
{
	return setRange(member.minimum, maximum);
}
BLong BLongSlider::maximum() const
{
	return member.maximum;
}

void BLongSlider::setStep(BLong step)
{
	if (step < 0)
		return;
	if (member.step != step)
	{
		member.step = step;
		emit(Signal_StepChanged, member.step);
	}
}
BLong BLongSlider::step() const
{
	return member.step;
}

void BLongSlider::increase()
{
	BLong value = member.value + member.step;
	this->setValue(value);
}
void BLongSlider::decrease()
{
	BLong value = member.value - member.step;
	this->setValue(value);
}

BSize BLongSlider::sizeHint() const
{
	BSize baseSize = member.handleSize;
	if (const BFont* font = this->realFont())
	{
		if (baseSize.width() < 1)
			baseSize.width() = font->size();
		if (baseSize.height() < 1)
			baseSize.height() = font->size();
	}
	if (member.orientation & Orientation_Horizontal)
		return this->preferSize(baseSize.width() * 10, baseSize.height());
	if (member.orientation & Orientation_Vertical)
		return this->preferSize(baseSize.width(), baseSize.height() * 10);
	return this->preferSize(baseSize);
}

void BLongSlider::resizeEvent(const BEvent& event)
{
	member.dirty = true;
	BWidget::resizeEvent(event);
}

void BLongSlider::styleEvent(const BEvent& event)
{
	BWidget::styleEvent(event);

	const BFont* font = this->realFont();
	member.grooveSize = bMax(1, font->size() / 5);

	const BStyle* style = this->realStyle();

	style_member(style)->assign(Value_Graph, member.handleGraph);
	style_member(style)->assign(Value_Handle_Size, member.handleSize);
	style_member(style)->assign(Value_Groove_Size, member.grooveSize);
	style_member(style)->assign(Value_Orientation, member.orientation);

	BImage* grooveImage = style->image(Image_Groove);
	if (grooveImage)
		member.grooveImage = grooveImage;

	member.dirty = true;
}
void BLongSlider::freshEvent(const BEvent& event)
{
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
	BWidget::freshEvent(event);
}
void BLongSlider::paintEvent(const BEvent& event)
{
	BPainter painter(this);
	painter.drawBackground();
	painter.drawBorder();
	painter.drawPerch();
	painter.drawMargin();

	State state = painter.state();
	if (member.hovered)
	{
		if (member.pressed)
			state |= State_Pressed;
		else
			state |= State_Hovered;
	}

	if (member.grooveImage)
	{
		painter.drawImage(member.grooveRect, member.grooveImage);
	}
	else if (painter.setColor(Color_Groove))
	{
		if (member.groovePoints.size())
			painter.fillTriangleFan(member.groovePoints);
		else
			painter.fillRect(member.grooveRect);
	}

	BImage* image = 0;
	if (BIcon* icon = this->icon())
		image = icon->image(state);
	if (image && painter.bind(image, member.handleRect))
	{
		if (member.handlePoints.size())
			painter.fillTriangleFan(member.handlePoints);
		else
			painter.fillRect(member.handleRect);
		painter.unbind();
	}
	else if (painter.setColor(Color_Handle, state))
	{
		if (member.handlePoints.size())
			painter.fillTriangleFan(member.handlePoints);
		else
			painter.drawGraph(member.handleRect % 80, member.handleGraph);
	}
}

void BLongSlider::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	if (member.hovered)
	{
		member.hovered = false;
		this->fresh();
	}
	BWidget::mouseEnterEvent(mouseEvent);
}
void BLongSlider::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	if (member.hovered)
	{
		member.hovered = false;
		this->fresh();
	}
	BWidget::mouseLeaveEvent(mouseEvent);
}

void BLongSlider::mousePressEvent(const BMouseEvent& mouseEvent)
{
	BPoint pos = mouseEvent.pos();
	if (mouseEvent.button() == Button_Left && !member.readOnly)
	{
		if (member.handleRect.contain(pos))
		{
			member.pressed = true;
			this->fresh();
		}
		else
		{
			BRect crect = this->centerRect();
			if (crect.contain(pos))
			{
				BLong value = member.valueOfPoint(pos);
				if (setValue(value))
				{
					emit(Signal_Sliding, member.value);
					emit(Signal_SlideFinished, member.value);
				}
			}
		}
	}
	BWidget::mousePressEvent(mouseEvent);
}
void BLongSlider::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left && !member.readOnly)
	{
		if (member.pressed)
		{
			member.pressed = false;
			emit(Signal_SlideFinished, member.value);
		}
		this->fresh();
	}
	BWidget::mouseReleaseEvent(mouseEvent);
}
void BLongSlider::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	BRect crect = this->centerRect();
	BPoint pos = mouseEvent.pos();
	if (member.pressed)
	{
		BLong value = member.valueOfPoint(pos);
		if (setValue(value))
		{
			emit(Signal_Sliding, member.value);
		}
	}
	else
	{
		bool hovered = member.handleRect.contain(pos);
		if (member.hovered != hovered)
		{
			member.hovered = hovered;
			this->fresh();
		}
	}
	BWidget::mouseMoveEvent(mouseEvent);
}
void BLongSlider::mouseWheelEvent(const BMouseEvent& mouseEvent)
{
	int whell = mouseEvent.wheel();
	if (setValue(member.value + whell * member.step))
	{
		emit(Signal_Sliding, member.value);
		emit(Signal_SlideFinished, member.value);
		mouseEvent.accept();
	}
	BWidget::mouseWheelEvent(mouseEvent);
}

